package com.jeady.jxcompose

import android.content.Context
import android.content.Intent
import android.os.Environment
import android.util.Base64
import android.util.Log
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.platform.LocalContext
import androidx.core.content.FileProvider
import com.google.gson.Gson
import java.io.File
import java.io.FileOutputStream
import java.net.URL
import java.net.URLEncoder

private val TAG = "[GitManager]"
class GitManager(private val repoOwner: String, private val repoName: String, private val accessToken: String) {
    //    private val server = ServerManager.getInstance("https://api.gitcode.com", 30L)
    private var server = ServerManager.getInstance("https://api.gitee.com", 60*10)
    fun setHost(url: String): Boolean{
        if(!url.startsWith("http") && !url.startsWith("https")) return false
        server =  ServerManager.getInstance(url)
        return true
    }
    fun getLatestServerVersion(filePath: String, latestVersionCallback: (VersionInfo?, errMsg:String?)->Unit){
        server.get("api/v5/repos/${repoOwner}/${repoName}/contents/" + URLEncoder.encode(filePath, "utf-8"),
            "access_token=${accessToken}", ResponseInfo::class.java
        ) { data, errMsg->
            Log.d(TAG, "getLatestServerVersion: err: $errMsg")
            data?.content?.let{
                val content = String(Base64.decode(it, Base64.DEFAULT))
                Gson().fromJson(content, AppVersionsInfo::class.java)?.let { appInfo->
//                        Log.d(TAG, "getServerVersionInfo: get versions $appInfo")
                    val versionInfo = appInfo.versions.find { vInfo->
                        return@find vInfo.version == appInfo.latestVersion
                    }
                    latestVersionCallback(versionInfo, null)
                }
            }?:latestVersionCallback(null, errMsg)
        }
    }
    fun getVersions(filePath: String, appVersions: (AppVersionsInfo)->Unit){
        server.get("api/v5/repos/${repoOwner}/${repoName}/contents/" + URLEncoder.encode(filePath, "utf-8"),
            "access_token=${accessToken}", ResponseInfo::class.java
        ) { data, errMsg->
            data?.content?.let{
                val content = String(Base64.decode(it, Base64.DEFAULT))
                Gson().fromJson(content, AppVersionsInfo::class.java)?.let { appInfo->
//                    Log.d(TAG, "getServerVersionInfo: get versions $appInfo")
                    appVersions(appInfo)
                }
            }
        }
    }
    fun hasNewVersion(oldVersion: String, newVersion: String) = newVersion>oldVersion
    fun downloadFile(apkPath: String, fileSavePath: String, downloadCallback: (String)->Unit){
        Log.d(TAG, "downloadFile: download from $apkPath")
        if(apkPath.startsWith("http") || apkPath.startsWith("https")){
            downloadFileByUrl(apkPath, fileSavePath, downloadCallback)
            return
        }
        val encodedPath = URLEncoder.encode(apkPath, "utf-8")
        server.get("api/v5/repos/${repoOwner}/${repoName}/contents/$encodedPath",
            "access_token=${accessToken}", ResponseInfo::class.java
        ) { data, errMsg->
            Log.i(TAG, "downloadFile: get contents res: ${data?.content?.length} / ${data?.size} ${data?.sha}; \nerr:$errMsg")
            data?.let { contentInfo ->
                if (contentInfo.content.isNotBlank() && contentInfo.content.length == contentInfo.size.toInt()){
                    saveByteArrayToFile(
                        fileSavePath,
                        Base64.decode(contentInfo.content, Base64.DEFAULT),
                        downloadCallback
                    )
                } else {
                    downloadBlobs(contentInfo.sha, fileSavePath, downloadCallback)
                }
            }?: downloadOnly(apkPath, fileSavePath.substringBefore(".apk")+".json", downloadCallback)
        }
    }
    fun downloadOnly(apkPath: String, fileSavePath: String, downloadCallback: (String)->Unit){
        val encodedPath = URLEncoder.encode(apkPath, "utf-8")
        server.get(
            "api/v5/repos/${repoOwner}/${repoName}/contents/$encodedPath",
            "access_token=${accessToken}", String::class.java
        ) { res, errMsg ->
            res?.let{
                saveByteArrayToFile(fileSavePath,it.toByteArray(), downloadCallback)
            }
        }
    }
    fun downloadBlobs(sha:String, fileSavePath: String, downloadCallback: (String)->Unit){
        Log.d(TAG, "downloadBlobs: download from $sha")
        server.get(
            "api/v5/repos/${repoOwner}/${repoName}/git/blobs/$sha",
            "access_token=${accessToken}", ResponseInfo::class.java
        ) { data, errMsg ->
            Log.i(
                TAG,
                "downloadBlobs: get blobs res: ${data?.content?.length} ; \nerr:$errMsg"
            )
            data?.let { blobsInfo->
                blobsInfo.content?.let {
                    saveByteArrayToFile(
                        fileSavePath,
                        Base64.decode(it, Base64.DEFAULT),
                        downloadCallback
                    )
                }?:run {
                    downloadCallback("下载失败: ${blobsInfo.error_msg}")
                }
            }
        }
    }
    private fun saveByteArrayToFile(fileSavePath: String, content: ByteArray, saveCallback: (String)->Unit){
        if(content.isEmpty()) return
        val apkFile = File(fileSavePath)
        if (apkFile.exists()){
            apkFile.delete()
        }else if(apkFile.parent?.let { File(it).exists() } == false){
            File(apkFile.parent).mkdirs()
        }
        try {
            val outputStream = FileOutputStream(apkFile)
            outputStream.write(content)
            outputStream.close()
            saveCallback(apkFile.absolutePath)
        }catch (e:Exception){
            Log.e(TAG, "downloadAPK: $e")
        }
    }
    fun downloadFileByUrl(url: String, fileSavePath: String, downloadCallback: (String)->Unit){
        if(!url.startsWith("http") && !url.startsWith("https")){
            downloadCallback("error Url")
            return
        }
        Log.d(TAG, "downloadFile: download from $url")
        val apkFile = File(fileSavePath)
        if (apkFile.exists()){
            apkFile.delete()
        }
        apkFile.createNewFile()
        val inputStream = URL(url).openStream()
        val outputStream = FileOutputStream(apkFile)
        val buffer = ByteArray(1024)
        var len = inputStream.read(buffer)
        Log.d(TAG, "downloadAPK: 开始下载")
        while (len != -1){
            outputStream.write(buffer, 0, len)
            len = inputStream.read(buffer)
            Log.d(TAG, "downloadAPK: 下载中 $len / ${apkFile.length()}")
        }
        Log.d(TAG, "downloadAPK: 下载完成")
        inputStream.close()
        outputStream.close()
        downloadCallback(apkFile.absolutePath)
    }
    // 打开apk文件进行更新
    fun openAPK(context: Context, apkPath:String){
        if(apkPath.isBlank()) return
        val intent = Intent(Intent.ACTION_VIEW)
        Log.d(TAG, "openAPK: get packageNmae ${context.packageName}")
        val uri = FileProvider.getUriForFile(
            context,
            "${context.packageName}.fileprovider",
            File(apkPath)
        )
        intent.setDataAndType(uri, "application/vnd.android.package-archive")
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
        intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
        context.startActivity(intent)
    }

    data class ResponseInfo(
        val timestamp: String,
        val status: Int,
        val type: String,
        val encoding: String,
        val size: String,
        val name: String,
        val path: String,
        val content: String,
        val sha: String,
        val url: String,
        val html_url: String,
        val download_url: String,
        val _links: ContentsInfoLinks,
        val error_msg: String,
        val error_code: String,
        val request_id: String
    )
    data class ContentsInfoLinks(val self: String, val html: String)

    data class AppVersionsInfo(
        val appId: String,
        val appLabel: String,
        val latestVersion: String,
        val versions: List<VersionInfo>,
    )
    data class VersionInfo(
        val appLabel: String,
        val version: String,
        val description: String,
        val iconUrl: String,
        val detailUrl: String,
        val downloadUrl: String,
        val pictureUrls: String,
        val publisher: PublisherInfo,
    )
    data class PublisherInfo(
        val name: String,
        val timestamp: String,
        val contact: String,
        val mark: String,
    )
}

@Composable
fun GitManagerExample(){
    val context = LocalContext.current
    val downloadDir: String= run{
        val downloadDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).path
        var saveDir = downloadDir + "/" + context.packageName
        if(!File(saveDir).exists()){
            if(!File(saveDir).mkdirs()){
                saveDir = downloadDir
            }
        }
        saveDir
    }
    var apkPath by remember {
        mutableStateOf("$downloadDir/release.apk")
    }
    val gitcodeManager = remember {
//            GitManager("jeadyu", "healthcare-publisher", "QnPoZQzjJDER4Kv6wKx8VLVQ") // gitcode
//            GitManager("jeadyu", "healthcare-publisher", "af19696ba3697a0d2831598268441d79") // gitee
        GitManager("jeady", "myPublisher", "af19696ba3697a0d2831598268441d79")
    }

    fun downloadFinish(state: String){
        val validState = state.endsWith(".apk") && File(state).exists()
        showTip("下载结束: $state", if(validState) "忽略" else "知道了", if(validState) "安装" else ""){
            if(it){
                gitcodeManager.openAPK(context, state)
            }
        }
    }
}